package com.example.common.utils;

import com.google.common.collect.Sets;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Stream;

/**
 * 时间工具类
 *
 * @description 建议使用 LocalDateTime 及 LocalDate 处理时间
 */
public class DateUtils {

    public static final String DATE_TIME_PATTERN1_Z = "yyyy-MM-dd'T'HH:mm:ss.SSS Z";
    public static final String DATE_TIME_yyyyMMdd = "yyyyMMdd";
    public static final String DATE_TIME_yyyyMM = "yyyyMM";
    public static final String DATE_TIME_yyyy_MM = "yyyy-MM";

    public static final String DATE_FORMAT_FULL = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_FORMAT_YMD = "yyyy-MM-dd";
    public static final String DATE_FORMAT_HMS = "HH:mm:ss";
    public static final String DATE_FORMAT_HM = "HH:mm";
    public static final String DATE_FORMAT_YMDHM = "yyyy-MM-dd HH:mm";
    public static final String DATE_FORMAT_YMDHMS = "yyyyMMddHHmmss";
    public static final long ONE_DAY_MILLS = 3600 * 1000 * 24;
    public static final int WEEK_DAYS = 7;
    private static final int DATE_LENGTH = DATE_FORMAT_YMDHM.length();

    private static final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_FULL);
    private static final SimpleDateFormat sdf1 = new SimpleDateFormat(DATE_TIME_PATTERN1_Z);
    private static final SimpleDateFormat sdf_yyyyMMdd = new SimpleDateFormat(DATE_TIME_yyyyMMdd);
    private static final SimpleDateFormat sdf_yyyy_MM_dd = new SimpleDateFormat(DATE_FORMAT_YMD);
    private static final SimpleDateFormat sdf_yyyyMM = new SimpleDateFormat(DATE_TIME_yyyyMM);

    public static Date toDate(LocalDate localDate) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
        return Date.from(instant);
    }

    public static Date toDate(String str, String pattern) {
        LocalDate localDate = toLocalDate(str, pattern);
        return toDate(localDate);
    }

    public static Date toDate(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    public static LocalDateTime toLocalDateTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }

    public static LocalDate toLocalDate(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalDate();
    }

    public static String ofYearMonth(LocalDateTime localDateTime) {
        int year = localDateTime.getYear();
        int monthValue = localDateTime.getMonthValue();
        if (monthValue < 10) {
            return year + "0" + monthValue;
        }
        return year + "" + monthValue;
    }

    public static LocalDateTime toLocalDateTime(String str, String pattern) {
        return LocalDateTime.parse(str, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将字符串类型日期转换为 LocalDate 类型
     *
     * @param str     日期。例如 2021-12-10
     * @param pattern 解析模式。例如 yyyy-MM-dd
     * @return LocalDate
     */
    public static LocalDate toLocalDate(String str, String pattern) {
        return LocalDate.parse(str, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将 LocalDateTime 转换为字符串
     *
     * @param localDateTime 时间
     * @param pattern       期望时间格式
     * @return String
     */
    public static String toString(LocalDateTime localDateTime, String pattern) {
        return null == localDateTime ? "" : localDateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将 LocalDate 转换为字符串
     *
     * @param localDate 日期
     * @param pattern   期望日期格式
     * @return String
     */
    public static String toString(LocalDate localDate, String pattern) {
        return null == localDate ? "" : localDate.format(DateTimeFormatter.ofPattern(pattern));
    }

    public static String current(String pattern) {
        return DateUtils.toString(LocalDateTime.now(), pattern);
    }

    public static LocalDateTime toLocalDateTime(long timestamp) {
        return timestamp == 0 ? null : LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), TimeZone.getDefault().toZoneId());
    }

    public static String toString(long timestamp, String pattern) {
        LocalDateTime localDateTime = toLocalDateTime(timestamp);
        return toString(localDateTime, pattern);
    }

    public static String toString(LocalDate localDate) {
        return toString(localDate,DATE_FORMAT_YMD);
    }

    /**
     * 获取上周第一天
     *
     * @param localDate 时间日期
     * @return LocalDate
     */
    public static LocalDate ofLastWeekStartDay(LocalDate localDate) {
        int value = localDate.getDayOfWeek().getValue();
        if (value == 7) {
            return localDate.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));
        }
        return localDate.minusDays(7).with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));
    }

    /**
     * 获取上周最后一天
     *
     * @param localDate 时间日期
     * @return LocalDate
     */
    public static LocalDate ofLastWeekEndDay(LocalDate localDate) {
        return localDate.minusDays(7).with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
    }

    /**
     * 获取本周最后一天
     *
     * @param localDate 时间日期
     * @return LocalDate
     */
    public static LocalDate ofCurrentWeekEndDay(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
    }

    /**
     * 获取上个月第一天
     *
     * @param localDate 时间日期
     * @return LocalDate
     */
    public static LocalDate ofLastMonthStartDay(LocalDate localDate) {
        if (localDate.getMonthValue() == 1) {
            return LocalDate.of(localDate.getYear() - 1, 12, 1);
        }
        return LocalDate.of(localDate.getYear(), localDate.getMonthValue() - 1, 1);
    }

    /**
     * 获取上个月最后一天
     *
     * @param localDate 时间日期
     * @return LocalDate
     */
    public static LocalDate ofLastMonthEndDay(LocalDate localDate) {
        if (localDate.getMonthValue() == 1) {
            LocalDate lastMonth = LocalDate.of(localDate.getYear() - 1, 12, 1);
            return lastMonth.with(TemporalAdjusters.lastDayOfMonth());
        }
        LocalDate lastMonth = LocalDate.of(localDate.getYear(), localDate.getMonthValue() - 1, 1);
        return lastMonth.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 获取起止日期间每一天日期列表
     *
     * @param start 起始日期。如 2021-12-06
     * @param end   结束日期。如 2021-12-24
     * @return list
     */
    public static List<String> betweenDates(String start, String end) {
        List<String> list = new ArrayList<>();
        LocalDate startDate = LocalDate.parse(start);
        LocalDate endDate = LocalDate.parse(end);
        long distance = ChronoUnit.DAYS.between(startDate, endDate);
        if (distance < 1) {
            return list;
        }
        Stream.iterate(startDate, d -> d.plusDays(1)).limit(distance + 1).forEach(f -> list.add(f.toString()));
        return list;
    }

    /**
     * 获取起止日期间每一天日期列表
     *
     * @param start 起始日期。如 2021-12-06
     * @param end   结束日期。如 2021-12-24
     * @return list
     */
    public static List<String> betweenDates(LocalDate start, LocalDate end) {
        List<String> list = new ArrayList<>();
        long distance = ChronoUnit.DAYS.between(start, end);
        if (distance < 1) {
            return list;
        }
        Stream.iterate(start, d -> d.plusDays(1)).limit(distance + 1).forEach(f -> list.add(f.toString()));
        return list;
    }

    /**
     * 将原时间中的 `-` 分隔符替换为 `/`。如 2021/12/06
     *
     * @param localDate 日期
     * @param pattern   日期格式
     * @return string
     */
    public static String replace(LocalDate localDate, String pattern) {
        return localDate.format(DateTimeFormatter.ofPattern(pattern)).replace("-", "/");
    }

    /**
     * 获取日期所在年的第一天
     *
     * @param localDate 日期
     * @return LocalDate 日期
     */
    public static LocalDate yearStart(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.firstDayOfYear());
    }

    /**
     * 获取日期所在年的最后一天
     *
     * @param localDate 日期
     * @return LocalDate 日期
     */
    public static LocalDate yearEnd(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.lastDayOfYear());
    }

    /**
     * 时间转化为毫秒数
     *
     * @param localDateTime 时间
     * @return Long 毫秒数
     */
    public static Long ofMilliseconds(LocalDateTime localDateTime) {
        return localDateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
    }

    /**
     * 毫秒转化为日期
     *
     * @param milliseconds 毫秒
     * @return LocalDate 日期
     */
    public static LocalDate toLocalDate(Long milliseconds) {
        return Instant.ofEpochMilli(milliseconds).atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * 返回截取掉秒的时间数组, 并且数组内元素按小时升序排序。[2022-01-01 00:00, 2022-01-01 01:00, 2022-01-01 02:00, ..., 2022-01-01 22:00, 2022-01-01 23:00]
     *
     * @param localDate 日期 2022-01-01
     * @return List
     */
    public static List<String> dayWithHourList(LocalDate localDate) {
        ArrayList<String> hours = new ArrayList<>();
        for (int i = 0; i <= 23; i++) {
            LocalDateTime localDateTime = localDate.atTime(i, 0);
            String hour = localDateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT_YMDHM));
            hours.add(hour);
        }
        return hours;
    }

    /**
     * 返回如：[2022-01, 2022-02, 2022-03, ..., 2022-11, 2022-12]
     *
     * @param localDate 日期 2022-01-01
     * @return List
     */
    public static List<String> yearWithMonthList(LocalDate localDate) {
        int year = localDate.getYear();
        ArrayList<String> list = new ArrayList<>();
        Month[] months = Month.values();
        for (Month month : months) {
            LocalDate date = LocalDate.of(year, month, 1);
            String format = date.format(DateTimeFormatter.ofPattern(DATE_TIME_yyyy_MM));
            list.add(format);
        }
        return list;
    }

    public static Set<String> genDate(Integer index) {
        Set<String> dateList = Sets.newHashSet();
        for (int i = 0; i < index; i++) {
            LocalDate now = LocalDate.now();
            LocalDate localDate = now.minusDays(i);
            dateList.add(DateUtils.toString(localDate, DateUtils.DATE_FORMAT_YMD));
        }
        return dateList;
    }

}

